package core;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import model.Authentication;
import model.FacebookEvent;

import com.google.gdata.client.calendar.CalendarQuery;
import com.google.gdata.client.calendar.CalendarService;
import com.google.gdata.data.DateTime;
import com.google.gdata.data.Link;
import com.google.gdata.data.PlainTextConstruct;
import com.google.gdata.data.batch.BatchOperationType;
import com.google.gdata.data.batch.BatchStatus;
import com.google.gdata.data.batch.BatchUtils;
import com.google.gdata.data.calendar.CalendarEntry;
import com.google.gdata.data.calendar.CalendarEventEntry;
import com.google.gdata.data.calendar.CalendarEventFeed;
import com.google.gdata.data.calendar.CalendarFeed;
import com.google.gdata.data.calendar.ColorProperty;
import com.google.gdata.data.calendar.HiddenProperty;
import com.google.gdata.data.extensions.When;
import com.google.gdata.data.extensions.Where;
import com.google.gdata.util.AuthenticationException;
import com.google.gdata.util.ServiceException;

public class GCal {

	private CalendarService myService;
	private CalendarEntry myCalendarEntry;
	
	public static final String calendarTitle = "Facebook Events";

	public GCal(Authentication auth) {
		try {
			myService = new CalendarService("jada-fbeventstogcal-1.0");
//			System.out.println("Trying to exchange authSubToken for sessionToken: "
//							+ auth.getAuthSub());
//			String sessionToken = AuthSubUtil.exchangeForSessionToken(
//					auth.getAuthSub(), null);
//			myService.setAuthSubToken(sessionToken, null);
			myService.setUserCredentials(auth.getEmail(), auth.getAuthSub());
			// ///TESTING FIXME
			
		} catch (AuthenticationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
//		} catch (IOException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		} catch (GeneralSecurityException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		} 
	}

	public void createCalendar() {
		try {
			// Create the calendar
			CalendarEntry calendar = new CalendarEntry();
			calendar.setTitle(new PlainTextConstruct(calendarTitle));
			calendar.setSummary(new PlainTextConstruct("This calendar contains your Facebook Events"));
			calendar.setHidden(HiddenProperty.FALSE);
			calendar.setColor(new ColorProperty("#2952A3"));

			System.err.println("Creating new calendar " + calendar.toString());
			// Insert the calendar
			URL postUrl = new URL("https://www.google.com/calendar/feeds/default/owncalendars/full");
			myCalendarEntry = myService.insert(postUrl, calendar);
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ServiceException e) {
			e.printStackTrace();
		}
	}
	
	public boolean getCalendar() {
		CalendarFeed resultFeed = null;
		boolean alreadyHaveCalendar = false;
		try {
			// Send the request and print the response
			URL feedUrl = new URL(
					"https://www.google.com/calendar/feeds/default/owncalendars/full");
			resultFeed = myService.getFeed(feedUrl, CalendarFeed.class);
			System.out.println("Your calendars:");
			System.out.println();
			for (int i = 0; i < resultFeed.getEntries().size(); i++) {
				CalendarEntry entry = resultFeed.getEntries().get(i);
				System.out.println("\t" + entry.getTitle().getPlainText());
				if( entry.getTitle().getPlainText().equals(GCal.calendarTitle) )
				{
					System.out.println("Calendar already exists " + entry.toString());
					myCalendarEntry = entry;
					deleteEvents();
					alreadyHaveCalendar = true;
					break;
				}
			}

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ServiceException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return alreadyHaveCalendar;
	}
	
	private void deleteEvents()
	{
		try {
//			System.out.println("Attempting to delete all future calendar events from " + myCalendarEntry.getEditLink().getHref());
	
			CalendarQuery myQuery = new CalendarQuery(getEditURL());
			myQuery.setMinimumStartTime(new DateTime(new Date()));
			// Fix for 25 result max and batch delete submission taken from:
			// http://stackoverflow.com/questions/2646642/google-calendar-query-returns-at-most-25-entries
			// Override the default 25 results max returned per query
			myQuery.setMaxResults(5000);
			
			int startIndex = 1;
			int entriesReturned;

			List<CalendarEventEntry> allCalEntries = new ArrayList<CalendarEventEntry>();

			CalendarEventFeed resultEventFeed;
			
			// Run our query as many times as necessary to get all the
			// Google calendar entries we want
			while (true) {
			    myQuery.setStartIndex(startIndex);

			    // Execute the query and get the response
			    resultEventFeed = myService.query(myQuery, CalendarEventFeed.class);

			    entriesReturned = resultEventFeed.getEntries().size();
			    if (entriesReturned == 0)
			        // We've hit the end of the list
			        break;

			    // Add the returned entries to our local list
			    allCalEntries.addAll(resultEventFeed.getEntries());

			    startIndex = startIndex + entriesReturned;
			}

			// Delete all the entries as a batch delete
			CalendarEventFeed batchRequest = new CalendarEventFeed();
			
			System.out.println("Result feed size: " + allCalEntries.size());
			for (int i = 0; i < allCalEntries.size(); i++) {
				
				CalendarEventEntry entry = allCalEntries.get(i);

			    BatchUtils.setBatchId(entry, Integer.toString(i));
			    BatchUtils.setBatchOperationType(entry, BatchOperationType.DELETE);
			    batchRequest.getEntries().add(entry);
			}
			
			// Get the batch link URL and send the batch request
			Link batchLink = resultEventFeed.getLink(Link.Rel.FEED_BATCH, Link.Type.ATOM);
			CalendarEventFeed batchResponse = myService.batch(new URL(batchLink.getHref()), batchRequest);

			// Ensure that all the operations were successful
			boolean isSuccess = true;
			StringBuffer batchFailureMsg = new StringBuffer("These entries in the batch delete failed:");
			for (CalendarEventEntry entry : batchResponse.getEntries()) {
			    String batchId = BatchUtils.getBatchId(entry);
			    if (!BatchUtils.isSuccess(entry)) {
			        isSuccess = false;
			        BatchStatus status = BatchUtils.getBatchStatus(entry);
			        batchFailureMsg.append("\nID: " + batchId + "  Reason: " + status.getReason());
			    }
			}

			if (!isSuccess) {
			    throw new Exception(batchFailureMsg.toString());
			}
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ServiceException e) {
			e.printStackTrace();
		}
		catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public void addEvents(List<FacebookEvent> fbEvents)
	{
		try {
			// Delete all the entries as a batch delete
			CalendarEventFeed batchRequest = new CalendarEventFeed();
			CalendarEventFeed feed = myService.getFeed(getEditURL(), CalendarEventFeed.class);
			
			int i = 0;
			for(FacebookEvent event: fbEvents)
			{
				CalendarEventEntry entry = makeEvent(event);
				if(entry == null) continue; //In case we skip if event happens before
				BatchUtils.setBatchId(entry, Integer.toString(i++));
				BatchUtils.setBatchOperationType(entry, BatchOperationType.INSERT);
				batchRequest.getEntries().add(entry);
			}
			// Get the batch link URL and send the batch request
			Link batchLink = feed.getLink(Link.Rel.FEED_BATCH, Link.Type.ATOM);
			CalendarEventFeed batchResponse = myService.batch(new URL(batchLink.getHref()), batchRequest);
			
			// Ensure that all the operations were successful
			boolean isSuccess = true;
			StringBuffer batchFailureMsg = new StringBuffer("These entries in the batch insert failed:");
			for (CalendarEventEntry entry : batchResponse.getEntries()) {
				String batchId = BatchUtils.getBatchId(entry);
				if (!BatchUtils.isSuccess(entry)) {
					isSuccess = false;
					BatchStatus status = BatchUtils.getBatchStatus(entry);
					batchFailureMsg.append("\nID: " + batchId + "  Reason: " + status.getReason());
				}
			}
			
			if (!isSuccess) {
				throw new Exception(batchFailureMsg.toString());
			}
		}
		catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	private CalendarEventEntry makeEvent(FacebookEvent fbEvent)
	{
		if(fbEvent.getStartTime().compareTo(new Date()) < 0 )
		{
			System.err.println("Event is before now, will be ignored");
			return null;
		}
		CalendarEventEntry myEntry = new CalendarEventEntry();

		myEntry.setTitle(new PlainTextConstruct(fbEvent.getTitle()));
		myEntry.addLocation(new Where("", "", fbEvent.getLocation()));
		myEntry.setContent(new PlainTextConstruct(fbEvent.getLocation()));

		When eventTimes = new When();
		eventTimes.setStartTime(new DateTime(fbEvent.getStartTime()));
		eventTimes.setEndTime(new DateTime(fbEvent.getEndTime()));
		myEntry.addTime(eventTimes);

		return myEntry;
	}
	
	private URL getEditURL() {
		URL url = null;
		try {
			String editLink = myCalendarEntry.getId();
			System.out.println(editLink);
			String id = editLink.substring(editLink.lastIndexOf('/')+1);  //Parse the actual calendar id from this
	
			/*Construct the feed URL to the selected calendar*/
			return new URL("http://www.google.com/calendar/feeds/" + id + "/private/full");
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return url;
	}
	

}
